import json
import re
import sys
from pathlib import Path

import time
import os
import fitz
from openpyxl import load_workbook
from openpyxl.utils import column_index_from_string
import argparse


def get_replace_arr_from_excel(filepath):
    is_369 = False
    try:
        wb = load_workbook(filepath)
    except FileNotFoundError:
        print("文件未找到，请检查文件名是否正确!")
        sys.exit(1)
    sheet = wb["Sheet1"]
    min_col = column_index_from_string("A")
    max_col = column_index_from_string("C")
    replaceArr = []
    for row in sheet.iter_rows(min_row=1, max_row=100, min_col=min_col, max_col=max_col, values_only=True):
        if row[0]:
            new_text = str(row[1]) if row[1] else ''
            new_text = new_text.strip()
            # print(new_text)
            replaceArr.append([str(row[0]) if row[0] else '', new_text,str(row[2]) if row[2] else ''])
    #     if row[2] == "PDF主单号" and "369" in row[1]:
    #         is_369 = True
    # if not is_369:
    #     replaceArr.insert(0,["CONSOL", "", ""])
    #     replaceArr.insert(0,["CONSOLIDATED SHIPMENT", "", ""])
    #     replaceArr.insert(0,["DETAILS  AS PER CARGO", "", ""])
    #     replaceArr.insert(0,["MANIFEST  ATTACHED", "", ""])
    return replaceArr, is_369


def get_replace_arr_from_json(json_str):
    # print(json_str)
    is_369 = False
    json_str = json_str.replace("\n","\\n")
    arr = json.loads(json_str)
    replaceArr = []
    for m in arr:
        if m[0] == "PDF主单号" and "369" in m[1]:
            is_369 = True
    col_number = column_index_from_string("N")
    program_filepath = os.path.normpath(r"E:\Decl\VBA程序\分单\369新主单.xlsm")
    wb = load_workbook(program_filepath)
    sheet = wb["数据映射字典"]
    for row in sheet.iter_rows(min_row=3, max_row=13, min_col=col_number, max_col=col_number, values_only=True):
        if row[0]:
            replaceArr.append([row[0], "", ""])

    for item in arr:
        replaceArr.append([item[0], item[1], item[2]])
    return replaceArr,is_369

def trim_long_txt_eachLine(text,  max_len:int=63):
    full_str_arr = re.split(r'\n+', text)
    # print(full_str_arr)
    result = []
    for one_line_str in full_str_arr:
        if len(one_line_str) > max_len:
            one_line_str = trim_long_txt(one_line_str, max_len)
        result.append(one_line_str)
    # print(result)
    full_str = "\n".join(result).strip()
    return full_str

def trim_long_txt(text, max_len:int=63):
    full_str = text.replace('\n',' ')
    full_str_arr = re.split(r'[ \xa0]+', full_str)
    #print(full_str_arr)
    full_str = ""
    one_line_str = ""
    for i in range(len(full_str_arr)):
        if len(one_line_str +' '+ full_str_arr[i]) > max_len:
            full_str += "\n" + full_str_arr[i]
            one_line_str = full_str_arr[i]
        else:
            full_str += ' '+ full_str_arr[i]
            one_line_str += ' '+ full_str_arr[i]
        #print("full_str:",full_str)
    full_str = full_str.strip()
    return full_str

def edit_pdf_keep_original_style(input_pdf, output_pdf, replaceArr, is_369):
    # 打开原始 PDF
    try:
        input_doc = fitz.open(input_pdf)
    except FileNotFoundError:
        print("模板文件未找到，请检查文件名是否正确!")
        sys.exit(1)
    # 创建新 PDF
    output_doc = fitz.open()
    # 遍历每一页
    for page in input_doc:
        # 在另取新的PDF
        new_page = output_doc.new_page(
            width=page.rect.width,
            height=page.rect.height
        )
        # 复制原页内容到新页
        new_page.show_pdf_page(page.rect, input_doc, page.number)

        # 获取页面所有文本块（结构化数据）
        blocks = page.get_text("dict")["blocks"]
        specs_num=[item[1] for item in replaceArr if "规格" in item[2]][0].count(' ') + 1
        specs_line_count = 0
        def replace_text(arr):
            nonlocal specs_line_count
            for item in arr:
                tp = item[2] if item[2] else ''
                old_text = item[0]
                new_text = item[1]
                #print("{}:{}->{}".format(tp,old_text,new_text))
                # 存储找到的文本实例（内容、位置、字体、字号）
                matches = []
                if "NEW" not in tp:
                    # 记录原文本元信息
                    for block in blocks:
                        if "lines" in block:
                            for line in block["lines"]:
                                for span in line["spans"]:
                                    if old_text in span["text"]:
                                        matches.append({
                                            "type": tp,
                                            "bbox": span["bbox"],  # 文本位置（x0,y0,x1,y1）
                                            #"font": span["font"],  # 字体名称
                                            "size": span["size"],  # 字体大小
                                        })
                    # 先删除所有旧文本实例
                    for match in matches:
                        # print(match)
                        x0, y0, x1, y1 = match["bbox"]
                        rect = fitz.Rect(x0, y0, x1, y1)  # 转换为矩形区域
                        new_page.add_redact_annot(rect)  # 标记删除区域
                    if matches: new_page.apply_redactions()  # 应用所有删除
                else:
                    # print("position:",new_text, old_text)
                    p = json.loads(old_text)
                    if p:
                        # print(p)
                        matches.append({
                            "type": tp,
                            "bbox": (p['x0'],p['y0'],p['x1'],p['y1']),  # 文本位置（x0,y0,x1,y1）
                            # "font": span["font"],  # 字体名称
                            "size": p["size"],  # 字体大小
                        })

                # 插入新文本（按原始样式）
                if new_text:
                    for match in matches:
                        x0, y0, x1, y1  = match["bbox"]
                        size = match["size"]
                        y1 -= 3
                        if '品名' in tp:
                            count = new_text.count('\n') + 1
                            if count > 7:
                                width = 57
                            else:
                                width = 38
                            new_text = trim_long_txt(new_text, width)
                            # print(width)
                            # print(new_text)
                            x0 -= 3
                            y1 -= 35
                            size -= 3
                            # if specs_num > 12:
                            #     x0 -= 30
                        if '规格' in tp:
                            # print(specs_num)
                            x0 -= 7
                            y1 -= 30
                            size -= 3
                            # new_text_arr = new_text.split('||')
                            # seperator = '\n' if specs_num > 30 else '\n\n'
                            # 大于19，超出框--> 变成一行三列
                            # print(specs_num)
                            if specs_num > 40:
                                width = 82
                                x0 -= 162
                            elif specs_num > 24:#32
                                width = 61
                                x0 -= 95
                            elif specs_num > 8:
                                width = 42
                                x0 -= 2
                            else:
                                width = 21
                            new_text = trim_long_txt(new_text, width)
                            specs_line_count = new_text.count('\n') + 1
                        if '体积' in tp:
                            x0 += 11
                            offset = specs_line_count - 10
                            # print("offset:",offset)
                            y1 += 30 + (offset if offset>0 else 0) * 2
                            new_text = "MEAS: {} CBM".format(new_text)
                        if tp == "发货人":
                            x0 -= 3
                            new_text = trim_long_txt(new_text,58)
                            if new_text.count("\n") < 6: size += 1
                        if tp == "收货人":
                            y1 -= 20
                            # print(new_text.count("\n"))
                            if new_text.count("\n") > 4:
                                new_text = trim_long_txt(new_text, 58)
                                size -= 2
                            else:
                                new_text = trim_long_txt_eachLine(new_text, 58)
                        if "目的港" in tp:
                            size -= 1
                        if "通知信息" in tp:
                            new_text = trim_long_txt_eachLine(new_text, 58)
                            # print(new_text.count("\n") )
                            x0 -= 82
                            if new_text.count("\n") > 1:
                                y1 -= 45
                                size -= 5 if new_text.count("\n") <6 else 6
                        if "其它传输信息" in tp:
                            y1 -= 8
                            new_text = trim_long_txt_eachLine(new_text, 90)
                            x0 -= 22
                            size -= 4 if new_text.count("\n") <4 else 5
                        new_page.insert_text(
                            (x0, y1),  # 原始位置（左上角）
                            new_text,
                           # fontname=match["font"],  # 原始字体
                            fontsize=size,  # 原始字号
                        )
    replace_text([item for item in replaceArr if "pdf" in item[2]])
    replace_text([item for item in replaceArr if "pdf" not in item[2]])
    # 优化保存（清理冗余数据 + 压缩）
    output_doc.save(output_pdf, garbage=4, deflate=True)
    input_doc.close()
    output_doc.close()


def replace_text_keep_style(input_pdf, output_pdf, replaceArr, is_369):
    # 打开原始 PDF
    try:
        input_doc = fitz.open(input_pdf)
    except FileNotFoundError:
        print("模板文件未找到，请检查文件名是否正确!")
        sys.exit(1)
    # 创建新 PDF
    output_doc = fitz.open()
    # 遍历每一页
    for page in input_doc:
        # 在新 PDF 中添加空白页（尺寸与原页相同）
        new_page = output_doc.new_page(
            width=page.rect.width,
            height=page.rect.height
        )
        # 复制原页内容到新页
        new_page.show_pdf_page(new_page.rect, input_doc, page.number)
        # 查找所有匹配的文本实例
        for item in replaceArr:
            tp = item[2] if item[2] else ''
            old_text = item[0]
            new_text = item[1]
            if old_text == new_text: continue
            #if "航班日期" not in tp: continue
            # print("{}:{}->{}".format(tp,old_text,new_text))
            text_instances = page.search_for(old_text)

            for inst in text_instances:
                # 添加一个白色矩形覆盖文本->做清除
                # new_page.draw_rect(inst, color=(1, 1, 1), fill=(1, 1, 1), overlay=True)
                new_page.add_redact_annot(inst, fill=(1, 1, 1))  # 白色填充
                new_page.apply_redactions()
                if new_text: # 仅有新文本时做文本插入
                        # 提取原文本的字体、大小、颜色
                        text_blocks = page.get_text("dict")["blocks"]
                        for block in text_blocks:
                            if "lines" in block:
                                for line in block["lines"]:
                                    for span in line["spans"]:
                                        #print('span["text"]:',span["text"])
                                        if old_text in span["text"]:
                                            font = span["font"]  # 原字体（如 "Helvetica-Bold"）
                                            # Sanitize the font name
                                            font = ''.join(e for e in font if e.isalnum())
                                            size = span["size"]  # 原字号（如 12.0）
                                            color = span["color"]  # 原颜色（RGB 格式，如 (0, 0, 0) = 黑色
                                            break
                        if new_text:
                            # print("new_text:",repr(new_text))
                            # 默认在相同位置插入新文本，保持原样式
                            ## 收货人和规格等的位置特殊处理
                            if '规格' in tp:
                                if is_369:
                                    new_text = new_text.replace("DIMS(CM): ", "")
                                    position = (inst.x0, inst.y1 - 3 - 30)
                                else:
                                    position = (inst.x0, inst.y1 - 3 - 60)
                            else:
                                position = (inst.x0, inst.y1 - 3)
                            if tp == "收货人":
                                position = (inst.x0, inst.y1 - 3 - 20)
                                size -= 1
                            if "目的港" in tp:
                                size -= 2
                            new_page.insert_text(
                                position,  # 原文本位置（左下角坐标）
                                new_text,
                                fontsize=size,  # 原字号
                                # fontname=font,  # 原字体
                                color=color  # 原颜色
                            )

    # 保存新 PDF
    #print(output_pdf)
    output_doc.save(output_pdf, garbage=4, deflate=True)
    input_doc.close()
    output_doc.close()


def main():
    parser = argparse.ArgumentParser(description='修改 PDF 文件内容')
    parser.add_argument('-rule_excel', help='Excel 文件路径')
    parser.add_argument('-rule_json', help='JSON 字符串')
    parser.add_argument('-tpl', help='模板pdf 文件路径')
    parser.add_argument('-data_excel', help='数据源')
    args = parser.parse_args()

    if not args.rule_excel and not args.rule_json:
        parser.error('必须提供 -excel 或 -json 参数')
    if args.rule_excel:
        replaceArr, is_369 = get_replace_arr_from_excel(args.rule_excel)
    else:
        replaceArr, is_369 = get_replace_arr_from_json(args.rule_json)
    if not args.data_excel:
        dest_filePah = "E:\\Decl\\VBA程序\\分单\\sample\\"
        in_filePah = args.tpl if args.tpl else dest_filePah + "369-94369612.pdf"
        out_fileName = dest_filePah + "out_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".pdf"
    else:
        path = Path(args.data_excel)
        dest_filePah = str(path.parent)
        in_filePah = args.tpl if args.tpl else "E:\\Decl\\VBA程序\\分单\\sample\\PDF模板2.pdf"
        out_fileName = os.path.join(dest_filePah,"out_{}{}".format(path.stem, ".pdf"))
        #print(out_fileName)
        out_fileName = os.path.normpath(out_fileName)
        in_filePah = os.path.normpath(in_filePah)
    edit_pdf_keep_original_style(in_filePah, out_fileName, replaceArr, is_369)


if __name__ == "__main__":
    main()
    print("successful")